﻿using Microsoft.AspNetCore.Http;
using PmSoft.Core;
using PmSoft.Core.FileStorage;
using PmSoft.Web.Abstractions.Services;

namespace PmSoft.Web.Abstractions.Attachment;

/// <summary>
/// 附件管理服务，提供附件操作的核心逻辑
/// </summary>
public class AttachmentManagerService
{
	private readonly IApplicationContext _applicationContext;
	private readonly List<TenantStorageConfig> _tenantConfigs;
	private readonly IFileUploadService _uploadService;
	private readonly IAttachmentService _attachmentService;
	private readonly IFileStorageProvider _fileStorageProvider;

	/// <summary>
	/// 构造函数，注入必要的依赖
	/// </summary>
	/// <param name="applicationContext">应用程序上下文，提供客户端信息</param>
	/// <param name="uploadService">文件上传服务实例</param>
	/// <param name="tenantConfigs">租户存储配置列表</param>
	public AttachmentManagerService(IApplicationContext applicationContext,
		IFileUploadService uploadService, List<TenantStorageConfig> tenantConfigs,
		IAttachmentService? attachmentService,
		IFileStorageProvider fileStorageProvider)
	{
		_applicationContext = applicationContext;
		_uploadService = uploadService;
		_tenantConfigs = tenantConfigs;

		_fileStorageProvider = fileStorageProvider;

		if (attachmentService == null)
			throw new ArgumentNullException("请实现IAttachmentService接口才能使用AttachmentManagerService");
		_attachmentService = attachmentService;
	}

	/// <summary>
	/// 异步上传临时附件（使用字节数组）
	/// </summary>
	/// <param name="fileData">文件的字节数组内容</param>
	/// <param name="fileName">文件名，包含扩展名</param>
	/// <param name="tenantType">租户类型，例如 "Company" 或 "User"</param>
	/// <returns>上传成功的附件信息</returns>
	public async Task<IAttachment> UploadTemporaryAttachmentAsync(byte[] fileData, string fileName, string tenantType)
	{
		if (fileData == null || fileData.Length == 0)
			throw new ArgumentNullException(nameof(fileData), "文件数据不能为空。");
		if (string.IsNullOrWhiteSpace(fileName))
			throw new ArgumentNullException(nameof(fileName), "文件名不能为空。");

		var config = GetTenantConfig(tenantType);

		// 创建临时的 IFormFile 用于验证和兼容现有逻辑
		using var stream = new MemoryStream(fileData);
		var formFile = new FormFile(stream, 0, fileData.Length, "file", fileName)
		{
			Headers = new HeaderDictionary(),
			ContentType = GetMimeType(fileName)
		};

		ValidateFile(formFile, config);

		var extension = Path.GetExtension(formFile.FileName);
		var newFileName = $"{Guid.NewGuid()}{extension}";
		var bucketName = config.BucketName;
		var path = "temp";
		var objectName = $"{path}/{newFileName}";

		// 使用 IFileUploadService 上传字节数组
		await _uploadService.UploadFileAsync(formFile, bucketName, newFileName, path);

		var attachment = CreateAttachment(formFile, formFile.Length, tenantType, null, bucketName, objectName);
		await _attachmentService.CreateAsync(attachment);
		return attachment;
	}

	/// <summary>
	/// 异步上传临时附件（普通文件上传）
	/// </summary>
	/// <param name="file">上传的文件，包含文件内容和元数据</param>
	/// <param name="tenantType">租户类型，例如 "Company" 或 "User"</param>
	/// <returns>上传成功的附件信息</returns>
	public async Task<IAttachment> UploadTemporaryAttachmentAsync(IFormFile file, string tenantType)
	{
		var config = GetTenantConfig(tenantType);
		ValidateFile(file, config);
		var extension = Path.GetExtension(file.FileName);
		var fileName = $"{Guid.NewGuid()}{extension}";
		var bucketName = config.BucketName;
		var path = "temp";
		var objectName = $"{path}/{fileName}";

		await _uploadService.UploadFileAsync(file, bucketName, fileName, path);

		var attachment = CreateAttachment(file, file.Length, tenantType, null, bucketName, objectName);
		await _attachmentService.CreateAsync(attachment);
		return attachment;
	}

	/// <summary>
	/// 异步上传临时附件（分片上传）
	/// </summary>
	/// <param name="file">上传的分片文件</param>
	/// <param name="originalFileName">原始文件名称</param>
	/// <param name="fileName">系统生成的文件名称</param>
	/// <param name="tenantType">租户类型，例如 "Company" 或 "User"</param>
	/// <param name="chunkNumber">分片编号，从 1 开始</param>
	/// <param name="totalChunks">总分片数</param>
	/// <returns>分片完成时返回附件信息，未完成时返回 null</returns>
	public async Task<IAttachment?> UploadTemporaryAttachmentAsync(IFormFile file, string originalFileName, string fileName, string tenantType, int chunkNumber, int totalChunks)
	{
		if (chunkNumber < 1 || totalChunks < 1 || chunkNumber > totalChunks)
			throw new ArgumentException("分片参数无效。");

		if (string.IsNullOrEmpty(originalFileName))
			throw new ArgumentNullException(nameof(originalFileName), "原始文件名不能为空。");

		if (string.IsNullOrEmpty(fileName))
			throw new ArgumentNullException(nameof(fileName), "文件名不能为空。");

		var config = GetTenantConfig(tenantType);
		ValidateFile(file, config, fileName); // 分片也需要验证文件限制
		var bucketName = config.BucketName;
		var path = "temp"; // 分片上传最终合并到 temp 路径
		var objectName = $"{path}/{fileName}";
		// 调用 IFileUploadService 的分片上传方法
		var updateResult = await _uploadService.UploadChunkAsync(file, fileName, chunkNumber, totalChunks, bucketName, finalPath: path);
		if (updateResult.IsCompleted) // 分片完成，返回附件信息
		{
			var attachment = new TempAttachment
			{
				TenantType = tenantType,
				FileName = fileName,
				BucketName = bucketName,
				ObjectName = objectName,
				FriendlyName = originalFileName,
				MediaType = GetMediaType(fileName),
				MimeType = GetMimeType(fileName),
				FileSize = updateResult.TotalSize,
				UploaderIp = _applicationContext.ClientInfo.ClientIpAddress,
				Description = string.Empty,
				IsTemporary = true
			};
			await _attachmentService.CreateAsync(attachment);
			return attachment;
		}

		return null; // 分片未完成
	}

	/// <summary>
	/// 发布临时附件为正式附件
	/// </summary>
	/// <param name="attachmentId">附件ID</param>
	/// <param name="tenantId">租户ID</param>
	/// <param name="friendlyName">友好名称，可选，默认为原始文件名</param>
	/// <returns>更新后的附件信息</returns>
	public async Task<IAttachment> PublishAttachmentAsync(string attachmentId, string tenantId, string? friendlyName = null)
	{
		var attachment = await _attachmentService.GetAsync(attachmentId);
		if (attachment == null || !attachment.IsTemporary)
			throw new PmSoftException("附件不存在或不是临时附件。");


		attachment.TenantId = tenantId;
        string dateDir = DateTime.Now.ToString("yyyyMM");
        attachment.FriendlyName = friendlyName ?? attachment.FriendlyName;
		attachment.IsTemporary = false;

		var newObjectName = $"{attachment.TenantType}/{tenantId}/{dateDir}/{Path.GetFileName(attachment.ObjectName)}".ToLower();
		await _uploadService.MoveFileAsync(attachment.BucketName, attachment.ObjectName, attachment.BucketName, newObjectName);
		attachment.ObjectName = newObjectName;
		await _attachmentService.PublishAsync(attachment);
		return attachment;
	}

	/// <summary>
	/// 发布临时附件为正式附件（支持批量操作）
	/// </summary>
	/// <param name="attachments">附件参数列表</param>
	/// <returns>更新后的附件信息列表</returns>
	public async Task<List<IAttachment>> PublishAttachmentsAsync(List<PublishAttachmentArgs> attachments)
	{
		if (attachments == null || !attachments.Any())
			return new List<IAttachment>();

		var attachmentIds = attachments.Select(a => a.AttachmentId).ToList();
		var attachmentEntities = await _attachmentService.GetBatchAsync(attachmentIds);

		var tasks = new List<Task>();
		var updatedAttachments = new List<IAttachment>();

		foreach (var args in attachments)
		{
			// 直接在列表中查找匹配的附件
			var attachment = attachmentEntities
				.FirstOrDefault(a => a?.AttachmentId == args.AttachmentId && a.IsTemporary);

			if (attachment == null)
				continue; // 不存在或非临时，直接跳过

			// 更新附件属性
			attachment.TenantId = args.TenantId;
            string dateDir = DateTime.Now.ToString("yyyyMM");
            attachment.FriendlyName = args.FriendlyName ?? attachment.FriendlyName;
			attachment.IsTemporary = false;

			var newObjectName = $"{attachment.TenantType}/{args.TenantId}/{dateDir}/{Path.GetFileName(attachment.ObjectName)}".ToLower();

			tasks.Add(_uploadService.MoveFileAsync(
				attachment.BucketName,
				attachment.ObjectName,
				attachment.BucketName,
				newObjectName).ContinueWith(t =>
				{
					attachment.ObjectName = newObjectName;
					updatedAttachments.Add(attachment);
				}));
		}

		await Task.WhenAll(tasks);

		if (updatedAttachments.Any())
			await _attachmentService.PublishBatchAsync(updatedAttachments);

		return updatedAttachments;
	}



	/// <summary>
	/// 根据附件ID异步获取附件信息及文件流
	/// </summary>
	/// <param name="attachmentId">附件ID</param>
	/// <returns>附件信息和文件内容的组合对象</returns>
	public async Task<AttachmentWithContent?> GetAttachmentByIdAsync(string attachmentId)
	{
		// 从内存列表中查找附件
		var attachment = await _attachmentService.GetAsync(attachmentId);
		if (attachment == null)
			return null;

		// 从存储系统中读取文件内容
		var getFileArgs = new GetFileArgs
		{
			BucketName = attachment.BucketName,
			ObjectName = attachment.ObjectName
		};
		var fileData = await _fileStorageProvider.GetFileAsync(getFileArgs);
		if (fileData == null)
			throw new PmSoftException($"无法读取附件 {attachmentId} 的文件内容，文件可能已丢失。");

		// 返回包含附件信息和文件流的组合对象
		return new AttachmentWithContent
		{
			Attachment = attachment,
			Content = fileData.Data
		};
	}

	#region 私有方法

	/// <summary>
	/// 创建附件对象，复用普通上传和分片上传的逻辑
	/// </summary>
	/// <returns>附件信息</returns>
	private IAttachment CreateAttachment(IFormFile formFile, long totalSize, string tenantType, string? tenantId, string bucketName, string objectName)
	{
		return new TempAttachment
		{
			TenantType = tenantType,
			TenantId = tenantId,
			FileName = Path.GetFileName(objectName),
			BucketName = bucketName,
			ObjectName = objectName,
			FriendlyName = formFile.FileName,
			MediaType = GetMediaType(formFile.FileName),
			MimeType = GetMimeType(formFile.FileName),
			FileSize = totalSize,
			UploaderIp = _applicationContext.ClientInfo.ClientIpAddress,
			Description = formFile.ContentDisposition,
			IsTemporary = true
		};
	}

	/// <summary>
	/// 获取租户配置，如果未找到则使用默认配置
	/// </summary>
	private TenantStorageConfig GetTenantConfig(string tenantType)
	{
		return _tenantConfigs.FirstOrDefault(t => t.TenantType == tenantType)
			?? _tenantConfigs.First(t => t.TenantType == "default");
	}

	/// <summary>
	/// 验证文件是否符合租户配置限制
	/// </summary>
	private void ValidateFile(IFormFile file, TenantStorageConfig config, string? fileName = null)
	{
		if (file == null || file.Length == 0)
			throw new PmSoftException("未提供有效的文件。", "INVALID_FILE");

		if (config.MaxFileSize > 0 && file.Length > config.MaxFileSize)
			throw new PmSoftException($"文件大小超过限制 ({config.MaxFileSize / 1024 / 1024}MB)。", "FILE_SIZE_EXCEEDED");

		var ext = Path.GetExtension(string.IsNullOrEmpty(fileName) ? file.FileName : fileName).ToLower();
		if (config.AllowedExtensions.Any() && !config.AllowedExtensions.Contains(ext))
			throw new PmSoftException($"不支持的文件扩展名: {ext}。允许的扩展名: {string.Join(", ", config.AllowedExtensions)}", "INVALID_EXTENSION");
	}

    /// <summary>
    /// 根据文件名获取媒体类型
    /// </summary>
    private MediaType GetMediaType(string fileName)
    {
        var ext = Path.GetExtension(fileName).ToLower();
        return ext switch
        {
            ".jpg" or ".png" or ".gif" or ".jpeg" => MediaType.Image,
            ".pdf" or ".doc" or ".docx" or ".xls" or ".xlsx" => MediaType.Document,
            ".zip" or ".rar" or ".7z" => MediaType.Archive,
            ".mp4" or ".avi" or ".mov" => MediaType.Video,
            _ => MediaType.Unknown
        };
    }

    /// <summary>
    /// 根据文件名获取 MIME 类型
    /// </summary>
    private string GetMimeType(string fileName)
    {
        var ext = Path.GetExtension(fileName).ToLower();
        return ext switch
        {
            ".jpg" => "image/jpeg",
            ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".pdf" => "application/pdf",
            ".doc" => "application/msword",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".xls" => "application/vnd.ms-excel",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            ".zip" => "application/zip",
            ".7z" => "application/x-7z-compressed",
            ".rar" => "application/x-rar-compressed",
            ".mp4" => "video/mp4",
            ".mov" => "video/quicktime",
            ".avi" => "video/x-msvideo",
            _ => "application/octet-stream"
        };
    }

    #endregion
}